/*!
 * jQuery UI Menubar @VERSION
 * http://jqueryui.com
 *
 * Copyright 2013 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/menubar/
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 */
(function ($) {

  $.widget("ui.menubar", {
    version: "@VERSION",
    options: {
      items: "li",
      menus: "ul",
      icons: {
        dropdown: "ui-icon-triangle-1-s"
      },
      position: {
        my: "left top",
        at: "left bottom"
      },

      // callbacks
      select: null
    },

    _create: function () {
      // Top-level elements containing the submenu-triggering elem
      this.menuItems = this.element.children(this.options.items);

      // Links or buttons in menuItems, triggers of the submenus
      this.items = this.menuItems.children("button, a");

      // Keep track of open submenus
      this.openSubmenus = 0;

      this._initializeWidget();
      this._initializeMenuItems();
      this._initializeItems();
    },

    _initializeWidget: function () {
      this.element
        .addClass("ui-menubar ui-widget-header ui-helper-clearfix")
        .attr("role", "menubar");
      this._on({
        keydown: function (event) {
          var active;

          // If we are in a nested sub-sub-menu and we see an ESCAPE
          // we must close recursively.
          if (event.keyCode === $.ui.keyCode.ESCAPE &&
              this.active &&
              this.active.menu("collapse", event) !== true) {
            active = this.active;
            this.active.blur();
            this._close(event);
            $(event.target).blur().mouseleave();
            active.prev().focus();
          }
        },
        focusin: function () {
          clearTimeout(this.closeTimer);
        },
        focusout: function (event) {
          this.closeTimer = this._delay(function () {
            this._close(event);
            this.items.attr("tabIndex", -1);
            this.lastFocused.attr("tabIndex", 0);
          }, 150);
        },
        "mouseenter .ui-menubar-item": function () {
          clearTimeout(this.closeTimer);
        }
      });
    },

    _initializeMenuItems: function () {
      var subMenus,
        menubar = this;

      this.menuItems
        .addClass("ui-menubar-item")
        .attr("role", "presentation")
        // TODO why do these not work when moved to CSS?
        .css({
          "border-width": "1px",
          "border-style": "hidden"
        });

      subMenus = this.menuItems.children(menubar.options.menus).menu({
        position: {
          within: this.options.position.within
        },
        select: function (event, ui) {
          // TODO don't hardcode markup selectors
          ui.item.parents("ul.ui-menu:last").hide();
          menubar._close();
          ui.item.parents(".ui-menubar-item").children().first().focus();
          menubar._trigger("select", event, ui);
        },
        menus: this.options.menus
      })
        .hide()
        .attr({
          "aria-hidden": "true",
          "aria-expanded": "false"
        });

      this._on(subMenus, {
        keydown: function (event) {
          // TODO why is this needed?
          $(event.target).attr("tabIndex", 0);
          var parentButton,
            menu = $(this);
          // TODO why are there keydown events on a hidden menu?
          if (menu.is(":hidden")) {
            return;
          }
          switch (event.keyCode) {
            case $.ui.keyCode.LEFT:
              // TODO why can't this call menubar.previous()?
              parentButton = menubar.active.prev(".ui-button");

              if (this.openSubmenus) {
                this.openSubmenus--;
              } else if (this._hasSubMenu(parentButton.parent().prev())) {
                menubar.active.blur();
                menubar._open(event, parentButton.parent().prev().find(".ui-menu"));
              } else {
                parentButton.parent().prev().find(".ui-button").focus();
                menubar._close(event);
                this.open = true;
              }

              event.preventDefault();
              // TODO same as above where it's set to 0
              $(event.target).attr("tabIndex", -1);
              break;
            case $.ui.keyCode.RIGHT:
              this.next(event);
              event.preventDefault();
              break;
          }
        },
        focusout: function (event) {
          // TODO why does this have to use event.target? Is that different from currentTarget?
          $(event.target).removeClass("ui-state-focus");
        }
      });

      this.menuItems.each(function (index, menuItem) {
        menubar._identifyMenuItemsNeighbors($(menuItem), menubar, index);
      });

    },

    _hasSubMenu: function (menuItem) {
      return $(menuItem).children(this.options.menus).length > 0;
    },

    // TODO get rid of these - currently still in use in _move
    _identifyMenuItemsNeighbors: function (menuItem, menubar, index) {
      var collectionLength = this.menuItems.length,
        isFirstElement = (index === 0),
        isLastElement = (index === (collectionLength - 1));

      if (isFirstElement) {
        menuItem.data("prevMenuItem", $(this.menuItems[collectionLength - 1]));
        menuItem.data("nextMenuItem", $(this.menuItems[index + 1]));
      } else if (isLastElement) {
        menuItem.data("nextMenuItem", $(this.menuItems[0]));
        menuItem.data("prevMenuItem", $(this.menuItems[index - 1]));
      } else {
        menuItem.data("nextMenuItem", $(this.menuItems[index + 1]));
        menuItem.data("prevMenuItem", $(this.menuItems[index - 1]));
      }
    },

    _initializeItems: function () {
      var menubar = this;

      this._focusable(this.items);
      this._hoverable(this.items);

      // let only the first item receive focus
      this.items.slice(1).attr("tabIndex", -1);

      this.items.each(function (index, item) {
        menubar._initializeItem($(item), menubar);
      });
    },

    _initializeItem: function (anItem) {
      var menuItemHasSubMenu = this._hasSubMenu(anItem.parent());

      anItem
        .addClass("ui-button ui-widget ui-button-text-only ui-menubar-link")
        .attr("role", "menuitem")
        .wrapInner("<span class='ui-button-text'></span>");

      this._on(anItem, {
        focus: function () {
          anItem.attr("tabIndex", 0);
          anItem.addClass("ui-state-focus");
          //event.preventDefault();
        },
        focusout: function () {
          anItem.attr("tabIndex", -1);
          this.lastFocused = anItem;
          anItem.removeClass("ui-state-focus");
          //event.preventDefault();
        }
      });

      if (menuItemHasSubMenu) {
        this._on(anItem, {
          click: this._mouseBehaviorForMenuItemWithSubmenu,
          focus: this._mouseBehaviorForMenuItemWithSubmenu,
          mouseenter: this._mouseBehaviorForMenuItemWithSubmenu
        });

        this._on(anItem, {
          keydown: function (event) {
            switch (event.keyCode) {
              case $.ui.keyCode.SPACE:
              case $.ui.keyCode.UP:
              case $.ui.keyCode.DOWN:
                this._open(event, $(event.target).next());
                event.preventDefault();
                break;
              case $.ui.keyCode.LEFT:
                this.previous(event);
                event.preventDefault();
                break;
              case $.ui.keyCode.RIGHT:
                this.next(event);
                event.preventDefault();
                break;
              case $.ui.keyCode.TAB:
                break;
            }
          }
        });

        anItem.attr("aria-haspopup", "true");
        if (this.options.icons) {
          anItem.append("<span class='ui-button-icon-secondary ui-icon " + this.options.icons.dropdown + "'></span>");
          anItem.removeClass("ui-button-text-only").addClass("ui-button-text-icon-secondary");
        }
      } else {
        this._on(anItem, {
          click: function () {
            if (this.active) {
              this._close();
            } else {
              this.open = true;
              this.active = $(anItem).parent();
            }
          },
          mouseenter: function () {
            if (this.open) {
              this.stashedOpenMenu = this.active;
              this._close();
            }
          },
          keydown: function (event) {
            if (event.keyCode === $.ui.keyCode.LEFT) {
              this.previous(event);
              event.preventDefault();
            } else if (event.keyCode === $.ui.keyCode.RIGHT) {
              this.next(event);
              event.preventDefault();
            }
          }
        });
      }
    },

    // TODO silly name, too much complexity
    // TODO why is this used for three types of events?
    _mouseBehaviorForMenuItemWithSubmenu: function (event) {
      var isClickingToCloseOpenMenu, menu;

      // ignore triggered focus event
      if (event.type === "focus" && !event.originalEvent) {
        return;
      }
      event.preventDefault();

      menu = $(event.target).parents(".ui-menubar-item").children(this.options.menus);

      // If we have an open menu and we see a click on the menuItem
      // and the menu thereunder is the same as the active menu, close it.
      // Succinctly: toggle menu open / closed  on the menuItem
      isClickingToCloseOpenMenu = event.type === "click" &&
        menu.is(":visible") &&
        this.active &&
        this.active[0] === menu[0];

      if (isClickingToCloseOpenMenu) {
        this._close();
        return;
      }
      if (event.type === "mouseenter") {
        this.element.find(":focus").focusout();
        if (this.stashedOpenMenu) {
          this._open(event, menu);
        }
        this.stashedOpenMenu = undefined;
      }
      // If we already opened a menu and then changed to be "over" another MenuItem ||
      // we clicked on a new menuItem (whether open or not) or if we auto expand (i.e.
      // we expand regardless of click if there is a submenu
      if ((this.open && event.type === "mouseenter") || event.type === "click") {
        clearTimeout(this.closeTimer);
        this._open(event, menu);
        // Stop propagation so that menuItem mouseenter doesn't fire.  If it does it
        // takes the "selected" status off off of the first element of the submenu.
        event.stopPropagation();
      }
    },

    _destroy: function () {
      this.menuItems
        .removeClass("ui-menubar-item")
        .removeAttr("role")
        .css({
          "border-width": "",
          "border-style": ""
        });

      this.element
        .removeClass("ui-menubar ui-widget-header ui-helper-clearfix")
        .removeAttr("role")
        .unbind(".menubar");

      this.items
        .unbind(".menubar")
        .removeClass("ui-button ui-widget ui-button-text-only ui-menubar-link ui-state-default")
        .removeAttr("role")
        .removeAttr("aria-haspopup")
        .children(".ui-icon").remove();

      // TODO fix this
      if (false) {
        // Does not unwrap
        this.items.children("span.ui-button-text").unwrap();
      } else {
        // Does "unwrap"
        this.items.children("span.ui-button-text").each(function () {
          var item = $(this);
          item.parent().html(item.html());
        });
      }

      this.element.find(":ui-menu")
        .menu("destroy")
        .show()
        .removeAttr("aria-hidden")
        .removeAttr("aria-expanded")
        .removeAttr("tabindex")
        .unbind(".menubar");
    },

    _collapseActiveMenu: function () {
      if (!this.active.is(":ui-menu")) {
        return;
      }
      this.active
        .menu("collapseAll")
        .hide()
        .attr({
          "aria-hidden": "true",
          "aria-expanded": "false"
        })
        .closest(this.options.items).removeClass("ui-state-active");
    },

    _close: function () {
      if (!this.active) {
        return;
      }

      this._collapseActiveMenu();

      this.active = null;
      this.open = false;
      this.openSubmenus = 0;
    },

    _open: function (event, menu) {
      var menuItem = menu.closest(".ui-menubar-item");

      if (this.active && this.active.length &&
          this._hasSubMenu(this.active.closest(this.options.items))) {
        this._collapseActiveMenu();
      }

      menuItem.addClass("ui-state-active");
      // workaround when clicking a non-menu item, then hovering a menu, then going back
      // this way afterwards its still possible to tab back to a menubar, even if its
      // the wrong item
      // see also "click menu-less item, hover in and out of item with menu" test in menubar_core
      if (!this.lastFocused) {
        this.lastFocused = menu.prev();
      }

      this.active = menu
        .show()
        .position($.extend({
          of: menuItem
        }, this.options.position))
        .removeAttr("aria-hidden")
        .attr("aria-expanded", "true")
        .menu("focus", event, menu.children(".ui-menu-item").first())
        .focus();

      this.open = true;
    },

    next: function (event) {
      function shouldOpenNestedSubMenu() {
        return this.active &&
          this._hasSubMenu(this.active.closest(this.options.items)) &&
          this.active.data("uiMenu") &&
          this.active.data("uiMenu").active &&
          this.active.data("uiMenu").active.has(".ui-menu").length;
      }

      if (this.open) {
        if (shouldOpenNestedSubMenu.call(this)) {
          // Track number of open submenus and prevent moving to next menubar item
          this.openSubmenus++;
          return;
        }
      }
      this.openSubmenus = 0;
      this._move("next", event);
    },

    previous: function (event) {
      if (this.open && this.openSubmenus) {
        // Track number of open submenus and prevent moving to previous menubar item
        this.openSubmenus--;
        return;
      }
      this.openSubmenus = 0;
      this._move("prev", event);
    },

    _move: function (direction, event) {
      var closestMenuItem = $(event.target).closest(".ui-menubar-item"),
        nextMenuItem = closestMenuItem.data(direction + "MenuItem"),
        focusableTarget = nextMenuItem.find(".ui-button");

      if (this.open) {
        if (this._hasSubMenu(nextMenuItem)) {
          this._open(event, nextMenuItem.children(".ui-menu"));
        } else {
          this._collapseActiveMenu();
          nextMenuItem.find(".ui-button").focus();
          this.open = true;
        }
      } else {
        closestMenuItem.find(".ui-button");
        focusableTarget.focus();
      }
    }

  });

}(jQuery));
